home *** CD-ROM | disk | FTP | other *** search
- /* ModelSavyController.m created by cfeder on Tue 01-Nov-1994 */
-
- /*
- Interesting master-detail relationships:
- M Detail Insert rule Delete Types
- PK -> PK D->PK := M->PK delete D to-one
- FK -> PK M->FK := D->PK M->FK=NULL to-one
- PK -> FK D->FK := M->PK Delete D to-many
- Many to many create link obj Delete link to-many
- */
- #import "SavvyControllerDelegate.h"
- #import "RelationshipKeySetter.h"
- #import "ResetRelationships.h"
- #import "KeyGenerator.h"
- #import "ValueForKey.h"
-
-
- @interface EODetailDatabaseDataSource (databaseChannel)
- - (EODatabaseChannel *)databaseChannel;
- @end
-
- @implementation EODetailDatabaseDataSource (databaseChannel)
- - (EODatabaseChannel *)databaseChannel
- {
- return [[self masterDataSource] databaseChannel];
- }
- @end
-
-
-
- @implementation SavvyControllerDelegate
-
- - (EORelationship *)detailRelationshipForController:controller;
- {
- // Look up
- id dataSource = [controller dataSource];
- if ([dataSource respondsToSelector:@selector(masterObject)]) {
- EOEntity *entity = [dataSource entity];
- id masterObject = [dataSource masterObject];
- EOEntity *masterEntity = [[entity model] entityForObject:masterObject];
- return [masterEntity relationshipNamed:[dataSource detailKey]];
- }
- return nil;
- }
-
- - (void)updateDetailObjectsForController:(EOController *)controller
- {
- EORelationship *relationship = [self detailRelationshipForController:controller];
- if (relationship) {
- // if this is a detail controller, then we may have to update
- // the relationship property of the master EO, since we
- // may have supressed insert and delete operations bound
- // for the detailDataSource
- id masterObject = [(id)[controller dataSource] masterObject];
- NSArray *objects = [controller allObjects];
- if ([relationship isToMany]) {
- objects = [[[NSArray alloc] initWithArray:objects] autorelease];
- } else {
- objects = [objects count] ? [objects objectAtIndex:0] : [EONull null];
- }
- [masterObject takeValue:objects forKey:[relationship name]];
- }
- }
-
-
-
- // EOController delegation methods
-
- - (BOOL)controller:(EOController *)controller willInsertObject:object atIndex: (unsigned)newIndex;
- {
- // Handles primary key assignment for newly inserted objects.
- EOEntity *entity = [(id)[controller dataSource] entity];
- [object assignPrimaryKeyIfNotAlreadyPresentForEntity:entity];
-
- return YES;
- }
-
- // Handles insertions into a detail controller. The tricky part here is
- // that if we assign a new department in the detail controller for
- // an employee, we don't want to actually add the department to the
- // database (it's already there). Instead, we want to update the foreign
- // key in the master object to point at the new department.
- // Similarly if we insert a project in the toProjects (many-to-many) detail
- // controller, we don't want to insert the project, but rather a "link"
- // object in the intermediate table emp_projects that joins employee and
- // project.
- - (EODataSourceOperationDelegateResponse)controller:(EOController *)controller
- willInsertObject:object
- inDataSource:dataSource;
- {
- EODataSourceOperationDelegateResponse response = EOPerformDataSourceOperation;
- EORelationship *relationship = [self detailRelationshipForController:controller];
-
- // This stuff only works if our dataSource is an EODetailDatabaseDataSource.
- // Otherwise we just pass the operation through
- if (relationship) {
- id masterObject = [dataSource masterObject];
- id modifiedObject;
- EODatabaseDataSource *masterDataSource = [dataSource masterDataSource];
-
- // Invoke the magic of the ModelRelExtensions to figure out what to
- // do for this relationship
- modifiedObject = [relationship updateKeysForSourceObject:masterObject destinationObject:object];
-
- if (modifiedObject == object) {
- modifiedObject = nil;
- } else if (modifiedObject == masterObject) {
- // we need to update this one as well
- NSLog(@"Updating master object '%@'", masterObject);
- if (![masterDataSource updateObject:masterObject])
- response = EORollbackDataSourceOperation; // put up error?
- } else {
- // link object that we need to insert
- NSLog(@"Inserting link object '%@'", modifiedObject);
- if (![masterDataSource insertObject:modifiedObject])
- response = EORollbackDataSourceOperation; // put up error?
- }
-
- if (modifiedObject) {
- // do we really need to insert this object? Or did the insert just indicate
- // a reference to an existing object?
- EODatabaseContext *context = [[dataSource databaseChannel] databaseContext];
- if ([context snapshotForObject:object])
- response = EODiscardDataSourceOperation;
- }
- }
- if (response == EOPerformDataSourceOperation)
- NSLog(@"Allowing insert of object '%@'", object);
- else {
- NSLog(@"Suppressing insert of object: '%@'", object);
- }
- return response;
- }
-
- - (EODataSourceOperationDelegateResponse)controller:(EOController *)controller
- willDeleteObject:object
- inDataSource:dataSource;
- {
- /*Deletion of detail record may dictate one of the following...
- M Detail Insert rule Delete Types
- PK -> PK D->PK := M->PK delete D to-one
- FK -> PK M->FK := D->PK M->FK=NULL to-one
- PK -> FK D->FK := M->PK Delete D to-many
- Many to many create link obj Delete link to-many
- */
- EODataSourceOperationDelegateResponse response = EOPerformDataSourceOperation;
- EORelationship *relationship = [self detailRelationshipForController:controller];
-
- // This stuff only works if our dataSource is an EODetailDatabaseDataSource.
- // Otherwise we just pass the operation through
- if (relationship) {
- EOEntity *entity = [relationship destinationEntity];
- id masterObject = [dataSource masterObject];
- id modifiedObject;
-
- // Invoke the magic of the ModelRelExtensions to figure out what to
- // do for this relationship
- modifiedObject = [relationship updateKeysForDeleteOfDestinationObject:object fromSourceObject:masterObject];
-
- if (modifiedObject == object) {
- // just go ahead and delete this one
- } else if (modifiedObject == masterObject) {
- // we need to update the master to reflect the delete
- // and leave this object alone
- response = EODiscardDataSourceOperation;
- NSLog(@"Updating master object '%@'", masterObject);
- if (![dataSource updateObject:masterObject])
- response = EORollbackDataSourceOperation; // put up error?
- } else {
- // we need to delete the link object
- // and leave this object alone
- EODatabaseContext *context = [[dataSource databaseChannel] databaseContext];
- EODatabase *database = [context database];
- EOEntity *linkEntity = [[entity model] entityForObject:modifiedObject];
- NSDictionary *pkDict;
- id registeredObject;
-
- response = EODiscardDataSourceOperation; // leave destination object alone
-
- // to delete the object we need to make sure that it's in the uniquing
- // tables. Check if it's already there, and if not, record this one
- pkDict = [modifiedObject valuesForKeys:[linkEntity primaryKeyAttributeNames]];
- registeredObject = [context objectForPrimaryKey:pkDict entity:linkEntity];
- if (!registeredObject) {
- [database recordObject:modifiedObject primaryKey:pkDict
- snapshot:pkDict];
- registeredObject = modifiedObject;
- }
- NSLog(@"Deleting link object '%@'", registeredObject);
- if (![[dataSource masterDataSource] deleteObject:registeredObject])
- response = EORollbackDataSourceOperation; // put up error?
- }
- }
- if (response == EOPerformDataSourceOperation)
- NSLog(@"Allowing delete of object '%@'", object);
- else {
- NSLog(@"Suppressing delete of object: '%@'", object);
- }
- return response;
- }
-
- - (void)controllerDidSaveToDataSource:(EOController *)controller
- {
- [self updateDetailObjectsForController:controller];
- }
-
- @end
-
-
- // This is probably evil.
- // This code checks modifications to foreign key properties in an EO, and
- // reassigns any corresponding relationship pointers using methods in
- // ResetRelationships.h.
- //
- // THIS CODE IS NOT ACTUALLY USED IN THE EXAMPLE PROGRAM, but is here as
- // an illustration.
- @implementation SavvyControllerDelegate (ResetRelationshipsForChangedKeyProps)
- static NSArray *changedKeys = nil;
-
- - (NSDictionary *)controller:(EOController *)controller willSaveEdits: (NSDictionary *)edits toObject:object
- {
- // update any related relationship properties (in case we update foreign keys)
- changedKeys = [edits allKeys];
- return edits;
- }
-
- - (void)controller:(EOController *)controller didSaveToObject:object
- {
- EODatabaseDataSource *ds = (EODatabaseDataSource *)[controller dataSource];
- EOEntity *entity = [ds entity];
-
- // update relationships for key changes
- [[ds databaseChannel] updateRelationshipsInObject:object forModifiedAttributes:[entity attributesNamed:changedKeys]];
-
- // update keys for relationship changes
- {
- int i, count = [changedKeys count];
- for(i=0; i<count; i++) {
- NSString *key = [changedKeys objectAtIndex:i];
- EORelationship *relationship = [entity relationshipNamed:key];
- if (relationship) {
- [relationship updateKeysForSourceObject:object
- destinationObject:[(NSObject *)object valueForKey:key]];
- }
- }
- }
- }
- @end
-